/*******************************************************************************
 * Copyright (c) 2003 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: Boris Pruessmann - Initial implementation
 ******************************************************************************/
package net.sourceforge.perforce.ui.action;

import java.util.Arrays;
import java.util.Comparator;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IPerforceServerManager;
import net.sourceforge.perforce.core.resources.IChangelist;
import net.sourceforge.perforce.core.resources.IP4File;
import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.dialogs.ChangeSpecDialog;
import net.sourceforge.perforce.ui.internal.Policy;
import net.sourceforge.perforce.ui.internal.model.PendingChangelistCache;
import net.sourceforge.perforce.ui.views.PendingChangelistView;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowPulldownDelegate2;

/**
 * @version $Revision: 489 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class ChangelistDropDownAction extends Action implements IMenuCreator,
    IWorkbenchWindowPulldownDelegate2, SelectionListener {
  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the menu */
  private Menu menu;
  /** Stores the changelist image. */
  private Image changelistImage;

  // -------------------------------------------------------------------------
  // Public Constructors

  public ChangelistDropDownAction() {
    setMenuCreator(this);

    final PerforceUIPlugin plugin = PerforceUIPlugin.getPlugin();
    final String descriptorId = IPerforceUIConstants.IMG_CLIENT_CHANGELIST;
    changelistImage = plugin.getImageDescriptor(descriptorId).createImage();
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  public void dispose() {
    changelistImage.dispose();
    changelistImage = null;

    if (menu != null) {
      menu.dispose();
      menu = null;
    }
  }

  public Menu getMenu(Control parent) {
    if (menu != null) {
      menu.dispose();
      menu = null;
    }

    menu = new Menu(parent);
    BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
      public void run() {
        final IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
        IPerforceServer[] servers = manager.getPerforceServers();
        Arrays.sort(servers, new Comparator<IPerforceServer>() {
          public int compare(IPerforceServer server1, IPerforceServer server2) {
            return server1.getUniqueName().compareTo(server2.getUniqueName());
          }

          @SuppressWarnings("unused")
          public boolean equals(IPerforceServer obj) {
            return false;
          }
        });

        for (int i = 0; i < servers.length; i++) {
          final IPerforceServer server = servers[i];
          PendingChangelistCache cache = PendingChangelistView.getPendingChangelistCache(server);

          final IChangelist[] changelists =
              cache.getChangelistElements(new Comparator<IChangelist>() {
                public int compare(IChangelist changelist1, IChangelist changelist2) {
                  return changelist1.getId().intValue() - changelist2.getId().intValue();
                }

                @Override
                public boolean equals(Object obj) {
                  IChangelist changelist = (IChangelist) obj;
                  return changelist.getClientName().equals(server.getClient());
                }
              });

          final MenuItem serverItem = new MenuItem(menu, SWT.NONE);
          serverItem.setText(getTextForServer(server));
          serverItem.setEnabled(false);

          final Integer activeChangelistId = server.getActiveChangelistId();
          for (int j = 0; j < changelists.length; j++) {
            final MenuItem item = new MenuItem(menu, SWT.CHECK);
            item.setText(getTextForChangelist(changelists[j]));
            item.setImage(changelistImage);
            item.setData(changelists[j]);
            item.addSelectionListener(ChangelistDropDownAction.this);

            if (activeChangelistId.equals(changelists[j].getId())) {
              item.setSelection(true);
            }
          }

          final MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
          menuItem.setText("New Changelist...");
          menuItem.setData(server);
          menuItem.addSelectionListener(ChangelistDropDownAction.this);

          if ((i + 1) < servers.length) {
            new MenuItem(menu, SWT.SEPARATOR);
          }
        }
      }
    });

    return menu;
  }

  public Menu getMenu(Menu parent) {
    return null;
  }

  public void init(IWorkbenchWindow window) {
  }

  public void run(IAction action) {
  }

  public void selectionChanged(IAction action, ISelection selection) {
  }

  public void widgetSelected(SelectionEvent e) {
    final MenuItem menuItem = (MenuItem) e.getSource();
    if (menuItem.getData() instanceof IChangelist) {
      final IChangelist changelist = (IChangelist) menuItem.getData();
      final int state = e.stateMask & (SWT.CTRL | SWT.SHIFT);
      if (state == SWT.NULL) {
        changelist.getServer().setActiveChangelistId(changelist.getId());
      } else if (state == SWT.CTRL) {
        if (!changelist.getId().equals(IChangelist.DEFAULT)) {
          editChangelist(changelist);
        }
      } else if (state == SWT.SHIFT) {
        submitChangelist(changelist);
      } else if (state == (SWT.CTRL | SWT.SHIFT)) {
        if (!changelist.getId().equals(IChangelist.DEFAULT)) {
          deleteChangelist(changelist);
        }
      }
    } else if (menuItem.getData() instanceof IPerforceServer) {
      final IPerforceServer server = (IPerforceServer) menuItem.getData();
      try {
        final ChangeSpecDialog dialog = ChangeSpecDialog.newInstance(getShell(), server);
        if (dialog.open() == ChangeSpecDialog.OK) {
          Integer changelistId = server.change(dialog.getChangeSpec());

          PerforceCorePlugin plugin = PerforceCorePlugin.getPlugin();
          plugin.broadcastChangelistStateChanges(new IPerforceServer[] {server});

          server.setActiveChangelistId(changelistId);
        }
      } catch (PerforceException pe) {
        ErrorDialog.openError(getShell(), Policy.bind("simpleInternal"),
            "Failed to create new changelist and set it active", pe.getStatus());
      }
    }
  }

  public void widgetDefaultSelected(SelectionEvent e) {
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Creates a menu label for the given server.
   *
   * @return the label for the server.
   */
  private String getTextForServer(IPerforceServer server) {
    final StringBuilder result = new StringBuilder();
    result.append(server.getPort()).append(' ');
    result.append(server.getClient()).append(' ');
    result.append(server.getUser());

    result.insert(0, ' ');
    while (result.length() < 50) {
      result.insert(0, '-');
    }

    return result.toString();
  }

  /**
   * Creates a menu label for the given changelist.
   *
   * @return the label for the given changelist.
   */
  private String getTextForChangelist(IChangelist changelist) {
    final StringBuilder result = new StringBuilder();

    final Integer id = changelist.getId();
    if (id.equals(IChangelist.DEFAULT)) {
      result.append("Default"); //$NON-NLS-1$
    } else {
      result.append("Change " + id); //$NON-NLS-1$
      result.append(" - "); //$NON-NLS-1$

      String description = changelist.getDescription();
      description = description.replace('\r', ' ');
      description = description.replace('\n', ' ');

      if (description.length() > 30) {
        description = description.substring(0, 27) + "..."; //$NON-NLS-1$
      }

      result.append('[').append(description).append(']');
    }

    return result.toString();
  }

  /**
   * Returns a shell.
   *
   * @return the shell.
   */
  private Shell getShell() {
    final IWorkbench workbench = PerforceUIPlugin.getPlugin().getWorkbench();
    return workbench.getActiveWorkbenchWindow().getShell();
  }

  /**
   * Brings up the submit dialog for the given changelist.
   *
   * @param changelist the changelist.
   */
  private void submitChangelist(final IChangelist changelist) {
    final SubmitAction action = new SubmitAction();
    action.selectionChanged(this, new StructuredSelection(new Object[] {changelist}));
    action.run(this);
  }

  /**
   * Brings up the edit dialog for the given changelist.
   *
   * @param changelist the changelist.
   */
  private void editChangelist(final IChangelist changelist) {
    final EditChangelistSpecAction action = new EditChangelistSpecAction();
    action.selectionChanged(this, new StructuredSelection(new Object[] {changelist}));
    action.run(this);
  }

  /**
   * Brings up the delete dialog for the given changelist.
   *
   * @param changelist the changelist.
   */
  private void deleteChangelist(final IChangelist changelist) {
    PendingChangelistCache cache =
        PendingChangelistView.getPendingChangelistCache(changelist.getServer());
    Object[] files = cache.getFileElements(new Comparator<IP4File>() {
      public int compare(IP4File o1, IP4File o2) {
        return 0;
      }

      @SuppressWarnings("unused")
      public boolean equals(IP4File file) {
        return file.getChangelistId().equals(changelist.getId());
      }
    });

    if (files.length == 0) {
      final DeleteEmptyChangelistAction action = new DeleteEmptyChangelistAction();
      action.selectionChanged(this, new StructuredSelection(new Object[] {changelist}));
      action.run(this);
    } else {
      final String message = "You cannot delete a non-empty changelist."; //$NON-NLS-1$
      MessageDialog.openError(getShell(), "Perforce Team Provider", message); //$NON-NLS-1$
    }
  }
}
